package com.vagavaga.tx;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;

/**
 * Transaction interceptor. This class intercepts all "wrapped"
 * {@link Connection} calls and manages transaction state for calling
 * thread.
 *
 * @author <code>river</code>
 * @version $Revision: 1.7 $ $Date: 2004/11/07 00:00:27 $
 */
class TransactionInterceptor extends TxInterceptor {

    /**
     * Original connection.
     */
    private final Connection connection;
    private final String dsName;
    /**
     * Constructor that creates TransactionInterceptor over given connection.
     * @param conn Connection instance to wrap.
     */
    public TransactionInterceptor(String name, Connection conn) {
        dsName = name;
        connection = conn;
    }

    /**
     * Implementation of invoke method specified in InvocationHandler.
     * This method is specific for transactions.
     *
     * @param object the proxy instance that the method was invoked on.
     * @param method method the <code>Method</code> instance corresponding to
     * the interface method invoked on the proxy instance. This interceptor
     * will only fire if {@link Connection#setAutoCommit(boolean)} method
     * is called.
     * @param args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or <code>null</code> if interface method takes no arguments.
     * @return the value to return from the method invocation on the
     * proxy instance.
     * @throws Throwable the exception to throw from the method
     * invocation on the proxy instance.
     */
    public Object invoke(Object object, Method method, Object[] args) throws
            Throwable {

        if (AUTOCOMMIT_METHOD.equals(method)) {
            autoCommit(((Boolean) args[0]).booleanValue());
            return null;
        }

        if (COMMIT_METHOD.equals(method)) {
            if (TransactionManager.isInTransaction(dsName)
                    && TransactionManager.getCurrentTransaction(dsName)
                    .isRollbackOnly()) {
                try {
                    ROLLBACK_METHOD.invoke(connection, (Object[]) null);
                } catch (InvocationTargetException ex) {
                    throw ex.getTargetException();
                }
                return null;
            }
        }

        if (CLOSE_METHOD.equals(method)) {
            TransactionManager.unbind(dsName);
        }

        try {
            return method.invoke(connection, args);
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

    /**
     * This method handles {@link Connection#autoCommit(boolean)} method
     * calls.
     * @param flag arg passed to autoCommit method.
     * @throws Throwable if original method threw exception, or
     * security manager don't alowe use of reflection.
     */
    private void autoCommit(boolean flag) throws Throwable {
        Object[] args = new Object[] {
                Boolean.valueOf(flag)};
        try {
            AUTOCOMMIT_METHOD.invoke(connection, args);
            if (!flag) {
                if (!TransactionManager.isInTransaction(dsName)) {
                    TransactionManager.bind(dsName, new Transaction(connection));
                }
            } else {
                if (TransactionManager.isInTransaction(dsName)) {
                    TransactionManager.unbind(dsName);
                }
            }
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}
